﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using BattleThis.Core.Movement;
using BattleThis.Core.Battle;

namespace BattleThis.Core
{

	/// <summary>
	/// Represent a BattleThis Game.
	/// </summary>
	public class Game
	{

		#region Variablen

		#endregion

		#region Properties
		/// <summary>
		/// Gets or sets the player list.
		/// </summary>
		/// <value>
		/// The player list.
		/// </value>
		private SortedList<int, Player> PlayerList { get; set; }

		/// <summary>
		/// Gets or sets the current player id.
		/// </summary>
		/// <value>
		/// The current player id.
		/// </value>
		private int CurrentPlayerId { get; set; }

		/// <summary>
		/// Gets the map of the game.
		/// </summary>
		public Map Map { get; private set; }

		/// <summary>
		/// Gets the players of the game.
		/// </summary>
		public IEnumerable<Player> Players { get { return this.PlayerList.Values; } }

		/// <summary>
		/// Gets the current player.
		/// </summary>
		public Player CurrentPlayer
		{
			get
			{
				if( this.IsReady() )
				{
					return this.PlayerList[ this.CurrentPlayerId ];
				}
				else return Player.Unkown;
			}
		
		}

		/// <summary>
		/// Gets the move service.
		/// </summary>
		public IMoveActionService Movement
		{
			get;
			set;
		}

		/// <summary>
		/// Gets or sets the battle service.
		/// </summary>
		/// <value>
		/// The battle service.
		/// </value>
		public BattleService Battle
		{
			get; set;
		}

		/// <summary>
		/// Gets or sets the name.
		/// </summary>
		/// <value>
		/// The name.
		/// </value>
		public string Name { get; set; }

		/// <summary>
		/// Gets or sets the id.
		/// </summary>
		/// <value>
		/// The id.
		/// </value>
		public int Id { get; protected set; }

		#endregion

		#region Methoden

		public Game( GameReference reference, Map map ) :this( reference,map, Enumerable.Empty<Player>())
		{}

		/// <summary>
		/// Initializes a new instance of the <see cref="Game"/> class.
		/// </summary>
		/// <param name="players">The players wich will play this game.</param>
		public Game( GameReference reference, Map map, IEnumerable<Player> players  )
		{
			if( players == null ) throw new ArgumentNullException( "players" );
			if( string.IsNullOrEmpty( reference.Name ) ) throw new ArgumentOutOfRangeException( "name is null or empty" );
			if( map == null ) throw new ArgumentNullException( "map" );


			this.Id = reference.Id;
			this.Name = reference.Name;
			this.CurrentPlayerId = 0;
			this.Map = map;
			this.PlayerList =  new SortedList<int, Player>();

			InitalizePlayerList( players );

			this.Movement = new MoveActionService( this.Map );
			this.Battle  = new BattleService( this );
			
		}

		/// <summary>
		/// Creates the player list.
		/// </summary>
		/// <param name="players">The players.</param>
		private void InitalizePlayerList( IEnumerable<Player> players )
		{
			var counter = 0;

			foreach( var player in players )
			{
				this.PlayerList.Add( counter, player );
				counter++;
			}
		}

		/// <summary>
		/// Change the player to the next.
		/// </summary>
		/// <returns></returns>
		public Player NextPlayer()
		{
			this.CurrentPlayer.GiveNewActionPoints();

			if( this.CurrentPlayerId < this.PlayerList.First().Key )
			{
				this.CurrentPlayerId = this.PlayerList.First().Key;
			}
			else
			{

				this.CurrentPlayerId +=1;

				if( this.CurrentPlayerId  >= this.PlayerList.Count() )
				{
					this.CurrentPlayerId  = this.CurrentPlayerId % this.PlayerList.Last().Key;
				}
			}

			var nextPlayer = this.PlayerList[ this.CurrentPlayerId ];

			return nextPlayer;
		}

		/// <summary>
		/// Determines whether this instance is fished.
		/// </summary>
		/// <returns>
		///   <c>true</c> if this instance is fished; otherwise, <c>false</c>.
		/// </returns>
		public bool IsFinished()
		{
			var inGamePlayer = this.PlayerList.Where( x => x.Value.HasUnits() );
			return inGamePlayer.Count() <= 1;
		}

		/// <summary>
		/// Determines whether this instance is ready for gaming.
		/// </summary>
		/// <returns>
		///   <c>true</c> if this instance is ready; otherwise, <c>false</c>.
		/// </returns>
		public bool IsReady()
		{
			return this.Map.MaxPlayer.Equals( this.PlayerList.Count );
		}

		/// <summary>
		/// Determines whether the specified unit has unit.
		/// </summary>
		/// <param name="unit">The unit.</param>
		/// <returns></returns>
		public Player HasUnit( Unit unit )
		{
			if( unit == null )throw new ArgumentNullException( "unit" );

			return this.Players.Where( x => x.HasUnit( unit ) ).Single();
		}

		/// <summary>
		/// Joins the specified new player.
		/// </summary>
		/// <param name="newPlayer">The new player.</param>
		public void Join( Player newPlayer )
		{
			if( newPlayer == null )throw new ArgumentNullException( "newPlayer" );

			if( this.IsReady() )
				throw new InvalidOperationException("Game is already ready.");

			if( this.PlayerList.Values.Contains( newPlayer ) )
				throw new InvalidOperationException( "Player is already in game" );

			int order = 0;
			if( this.PlayerList.Count > 0 )
			{ 
				order = this.PlayerList.Max( x => x.Key );
			}
			
			this.PlayerList.Add( order++, newPlayer );
		}

		#endregion

	}
}